﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using VMExplorer.Win32;
using VMExplorer.MemoryObjects;
using VMExplorer.Forms;

namespace VMExplorer.Controls
{
  partial class MemoryMap : UserControl
  {
    #region Private common fields

    private VMEDocument _doc = null;
		private bool _showMap = true;

		#endregion
		#region Private Map fields

		private Bitmap bmp = null;

    private int _iZoom = 5;
    private int _iNbCols;
    private int _iNbRowsViewed;
    private int _iFirstVisibleRow;
    private int _iLastVisibleRow;
    private int _iFirstVisiblePage;
    private int _iLastVisiblePage;

    private List<int> _navigation = new List<int>(64);
    private int _navIndex = 0;
    private bool _disableNavigation = false;

    private static readonly Pen _penInterline = new Pen(Color.FromArgb(0xE0, 0xE0, 0xE0));
    private static readonly Pen _penSelection = new Pen(Color.Yellow, 2);
    private static readonly Pen _penGroupSelected = new Pen (Color.FromArgb (0x00, 0x00, 0x40));
    private static readonly Pen _penGroupHovered = new Pen (Color.FromArgb (0x40, 0x40, 0x80));

    private static readonly Brush _brushStateFree = new SolidBrush(Color.White);

    private static readonly Brush _brushStateReserved = new SolidBrush(Color.FromArgb(0xC0, 0xC0, 0xC0));
    private static readonly Brush _brushStateReservedSelected = new SolidBrush(Color.FromArgb(0x80, 0x80,0x80));

    private static readonly Brush _brushStateCommited = new SolidBrush(Color.FromArgb(0xFF, 0x80, 0x80));
    private static readonly Brush _brushStateCommitedSelected = new SolidBrush(Color.FromArgb(0xFF, 0x00, 0x00));


    private static readonly Brush _brushTypeImage = new SolidBrush(Color.FromArgb(0xFF, 0x80, 0x80));
    private static readonly Brush _brushTypeImageSelected = new SolidBrush(Color.FromArgb(0xFF, 0x00, 0x00));

    private static readonly Brush _brushTypeMapped = new SolidBrush(Color.FromArgb(0xC0, 0xC0, 0xFF));
    private static readonly Brush _brushTypeMappedSelected = new SolidBrush(Color.FromArgb(0x00, 0x00, 0xFF));

    private static readonly Brush _brushTypePrivate = new SolidBrush(Color.FromArgb(0x80, 0xD0, 0x80));
    private static readonly Brush _brushTypePrivateSelected = new SolidBrush(Color.FromArgb(0x00, 0xD0, 0x00));


    private static readonly Brush _brushProtectNoAccess = new HatchBrush(HatchStyle.DiagonalCross, Color.FromArgb(0xFF, 0x80, 0x80), Color.White);
    private static readonly Brush _brushProtectNoAccessSelected = new HatchBrush(HatchStyle.DiagonalCross, Color.FromArgb(0xFF, 0x00, 0x00), Color.White);

    private static readonly Brush _brushProtectReadOnly = new SolidBrush(Color.FromArgb(0xFF, 0xFF, 0x70));
    private static readonly Brush _brushProtectReadOnlySelected = new SolidBrush(Color.FromArgb(0xFF, 0xFF, 0x00));

    private static readonly Brush _brushProtectReadWrite = new SolidBrush(Color.FromArgb(0x80, 0xD0, 0x80));
    private static readonly Brush _brushProtectReadWriteSelected = new SolidBrush(Color.FromArgb(0x00, 0xD0, 0x00));

    private static readonly Brush _brushProtectWriteCopy = new SolidBrush(Color.FromArgb(0xC0, 0xC0, 0xFF));
    private static readonly Brush _brushProtectWriteCopySelected = new SolidBrush(Color.FromArgb(0x00, 0x00, 0xFF));

    private static readonly Brush _brushProtectExecute = new SolidBrush(Color.FromArgb(0xFF, 0x80, 0x80));
    private static readonly Brush _brushProtectExecuteSelected = new SolidBrush(Color.FromArgb(0xFF, 0x00, 0x00));

    private static readonly Brush _brushProtectExecuteRead = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x80, 0x80), Color.FromArgb(0xFF, 0xFF, 0x70));
    private static readonly Brush _brushProtectExecuteReadSelected = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x00, 0x00), Color.FromArgb(0xFF, 0xFF, 0x00));

    private static readonly Brush _brushProtectExecuteReadWrite = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x80, 0x80), Color.FromArgb(0x80, 0xD0, 0x80));
    private static readonly Brush _brushProtectExecuteReadWriteSelected = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x00, 0x00), Color.FromArgb(0x00, 0xD0, 0x00));

    private static readonly Brush _brushProtectExecuteWriteCopy = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x80, 0x80), Color.FromArgb(0xC0, 0xC0, 0xFF));
    private static readonly Brush _brushProtectExecuteWriteCopySelected = new HatchBrush(HatchStyle.ForwardDiagonal, Color.FromArgb(0xFF, 0x00, 0x00), Color.FromArgb(0x00, 0x00, 0xFF));


		private static readonly Brush _brushIdentifiedRegion = new SolidBrush (Color.FromArgb (0xC0, 0xC0, 0xC0));
		private static readonly Brush _brushIdentifiedRegionSelected = new SolidBrush (Color.FromArgb (0x80, 0x80, 0x80));

		private static readonly Brush _brushUnidentifiedRegion = new SolidBrush (Color.FromArgb (0xFF, 0x80, 0x80));
		private static readonly Brush _brushUnidentifiedRegionSelected = new SolidBrush (Color.FromArgb (0xFF, 0x00, 0x00));


    private enum ViewMode
    {
      PageState,
      PageType,
      PageProtection,
			PageIdentification
    };
    ViewMode _viewMode = ViewMode.PageState;

    #endregion
    #region Private ListView fields

    private ListViewItem[] _lviCache;
    private int _firstCacheItem;
    private List<RegionInfo> _sortedList;

    #endregion

    public MemoryMap()
    {
      InitializeComponent();

      listView1.Columns[(int)ColumnDescriptor.Start].Tag = ColumnDescriptor.Start;
			listView1.Columns[(int)ColumnDescriptor.Size].Tag = ColumnDescriptor.Size;
			listView1.Columns[(int)ColumnDescriptor.Usage].Tag = ColumnDescriptor.Usage;
			listView1.Columns[(int)ColumnDescriptor.Description].Tag = ColumnDescriptor.Description;
			listView1.Columns[(int)ColumnDescriptor.State].Tag = ColumnDescriptor.State;
			listView1.Columns[(int)ColumnDescriptor.Protect].Tag = ColumnDescriptor.Protect;
			listView1.Columns[(int)ColumnDescriptor.Type].Tag = ColumnDescriptor.Type;
      listView1.Columns[(int)ColumnDescriptor.Base].Tag = ColumnDescriptor.Base;

		}

    public VMEDocument Document
    {
      get { return _doc; }
			set
			{
				if (_doc != null)
				{
					_doc.PageSelectionChanged -= _doc_PageSelectionChanged;
					_doc.RegionSelectionChanged -= _doc_RegionSelectionChanged;
					_doc.DataUpdated -= _doc_DataUpdated;
				}
				_doc = value;
				if (_doc != null)
				{
					_doc.PageSelectionChanged += _doc_PageSelectionChanged;
					_doc.RegionSelectionChanged += _doc_RegionSelectionChanged;
					_doc.DataUpdated += _doc_DataUpdated;
				}

				if (_showMap)
				{
					UpdateViewData ();
					UpdateScrollBar ();
					UpdateBitmapView ();
				}
				else
				{
					UpdateView ();
				}
			}
    }

    public bool AutoUpdate
    {
      get { return _btnAutoUpdate.Checked; }
      set { _btnAutoUpdate.Checked = value; }
		}

		#region Map related methods

		private void UpdateViewData()
    {
      _iNbCols = bmp!=null ? bmp.Width / _iZoom : 0;
      _iNbRowsViewed = bmp!=null ? bmp.Height / _iZoom : 0;
      _iFirstVisibleRow = _sb.Value;
      _iLastVisibleRow = _iFirstVisibleRow + _iNbRowsViewed;
      _iFirstVisiblePage = _iFirstVisibleRow * _iNbCols;
      _iLastVisiblePage = _iLastVisibleRow * _iNbCols;
    }

    private void UpdateScrollBar()
    {
      _sb.Minimum = 0;
      _sb.SmallChange = 1;

      // Change scrollbar range
      if (_doc != null && _doc.TotalPages > 0)
      {
        _sb.Maximum = _doc.TotalPages / _iNbCols;
        _sb.LargeChange = _iNbRowsViewed - 1;
      }
      else
      {
        _sb.Maximum = 0;
        _sb.LargeChange = 1;
      }

      // Ensure selction is visible
      if (_doc != null && _doc.SelectedPage != -1)
      {
        if (_doc.SelectedPage < _iFirstVisiblePage || _doc.SelectedPage >= _iLastVisiblePage)
        {
          int value = _doc.SelectedPage / _iNbCols - 1;
          if (value < _sb.Minimum) value = _sb.Minimum;
          else if (value > _sb.Maximum) value = _sb.Maximum;
          _sb.Value = value;
        }
      }

			_btnZoomIn.Enabled = (_iZoom <10);
			_btnZoomOut.Enabled = (_iZoom > 1);
    }

    private void UpdateBitmapView()
    {
      CreateBitmap();
      lock (bmp)
      {
        Graphics g = Graphics.FromImage(bmp);

        g.Clear(Color.Transparent);

        if (_doc == null) return;
        

        Brush brushCurrent = _brushStateFree;
        Pen penCurrent = _penInterline;

				ViewModeDelegate getCurrentBrush = null;
				switch (_viewMode)
				{
				case ViewMode.PageState: getCurrentBrush = ViewModePageState; break;
				case ViewMode.PageType: getCurrentBrush = ViewModePageType; break;
				case ViewMode.PageProtection: getCurrentBrush = ViewModePageProtection; break;
				case ViewMode.PageIdentification: getCurrentBrush = ViewModePageIdentification; break;
				}


        int iCurrentX = 0;
        int iCurrentY = 0;
        int iCurrentRow = 0;
        int iCurrentRegion = 0;
        foreach(RegionInfo ri in _doc.AddressRanges)
        {
          bool regionSelected = (ri == _doc.SelectedRange);
          bool groupSelected = !ri.Base.IsNull && ri.Base == _doc.SelectedRange.Base;
          bool groupHovered = !groupSelected && !ri.Base.IsNull && _pointedRegion != null && ri.Base == _pointedRegion.Base;

          int iNbPages = (int)ri.Size / 4096;

          while (iCurrentRow < _iFirstVisibleRow && iNbPages > 0)
          {
            if (iCurrentX + iNbPages >= _iNbCols)
            {
              int diff = _iNbCols - iCurrentX;
              iNbPages -= diff;
              iCurrentX = 0;
              iCurrentRow++;
            }
            else
            {
              iCurrentX += iNbPages;
              iNbPages = 0;
            }
          }

          
          if (iCurrentRow >= _iFirstVisibleRow && iCurrentRow < _iLastVisibleRow && iNbPages > 0)
          {
						brushCurrent = getCurrentBrush (ri, regionSelected);

            penCurrent = _penInterline;
            if(groupSelected) penCurrent = _penGroupSelected;
            if(groupHovered) penCurrent = _penGroupHovered;

            Rectangle[] pageRects = new Rectangle[iNbPages];
            for (uint iPage = 0; iPage < iNbPages; iPage++)
            {
              Rectangle thisRect = new Rectangle(iCurrentX * _iZoom, iCurrentY * _iZoom, _iZoom, _iZoom);
              pageRects[iPage] = thisRect;

              iCurrentX++;
              if (iCurrentX == _iNbCols)
              {
                iCurrentX = 0;
                iCurrentY++;
                iCurrentRow++;
                if (iCurrentRow == _iLastVisibleRow)
                  break;
              }
            }

            g.FillRectangles(brushCurrent, pageRects);
            if(_iZoom>2) g.DrawRectangles(penCurrent, pageRects);
          }

          iCurrentRegion++;
        }

        if (_doc.SelectedPage >= _iFirstVisiblePage && _doc.SelectedPage < _iLastVisiblePage)
        {
          int iRelative = _doc.SelectedPage - _iFirstVisiblePage;
          int iX = iRelative % _iNbCols;
          int iY = iRelative / _iNbCols;
          g.DrawRectangle(_penSelection, iX * _iZoom + 1, iY * _iZoom + 1, _iZoom - 1, _iZoom - 1);
        }

        g.Dispose();

        _pb.Refresh();
      }
    }

		private delegate Brush ViewModeDelegate (RegionInfo ri, bool regionSelected);

		private static Brush ViewModePageIdentification (RegionInfo ri, bool regionSelected)
		{
			Brush brushCurrent = null;
			if (ri.Usage == RegionUsage.Free)
				brushCurrent = _brushStateFree;
			else if (ri.Related.Count==0)
				brushCurrent = regionSelected ? _brushUnidentifiedRegionSelected : _brushUnidentifiedRegion;
			else
				brushCurrent = regionSelected ? _brushIdentifiedRegionSelected : _brushIdentifiedRegion;
			return brushCurrent;
		}

		private static Brush ViewModePageProtection (RegionInfo ri, bool regionSelected)
		{
			Brush brushCurrent = _brushStateFree;
			switch (ri.Protect)
			{
			case Native.MEMORY_PROTECTION.PAGE_NOACCESS: brushCurrent = regionSelected ? _brushProtectNoAccessSelected : _brushProtectNoAccess; break;
			case Native.MEMORY_PROTECTION.PAGE_READONLY: brushCurrent = regionSelected ? _brushProtectReadOnlySelected : _brushProtectReadOnly; break;
			case Native.MEMORY_PROTECTION.PAGE_READWRITE: brushCurrent = regionSelected ? _brushProtectReadWriteSelected : _brushProtectReadWrite; break;
			case Native.MEMORY_PROTECTION.PAGE_WRITECOPY: brushCurrent = regionSelected ? _brushProtectWriteCopySelected : _brushProtectWriteCopy; break;
			case Native.MEMORY_PROTECTION.PAGE_EXECUTE: brushCurrent = regionSelected ? _brushProtectExecuteSelected : _brushProtectExecute; break;
			case Native.MEMORY_PROTECTION.PAGE_EXECUTE_READ: brushCurrent = regionSelected ? _brushProtectExecuteReadSelected : _brushProtectExecuteRead; break;
			case Native.MEMORY_PROTECTION.PAGE_EXECUTE_READWRITE: brushCurrent = regionSelected ? _brushProtectExecuteReadWriteSelected : _brushProtectExecuteReadWrite; break;
			case Native.MEMORY_PROTECTION.PAGE_EXECUTE_WRITECOPY: brushCurrent = regionSelected ? _brushProtectExecuteWriteCopySelected : _brushProtectExecuteWriteCopy; break;
			}
			return brushCurrent;
		}

		private static Brush ViewModePageType (RegionInfo ri, bool regionSelected)
		{
			Brush brushCurrent = _brushStateFree;
			switch (ri.Type)
			{
			case Native.PAGE_TYPE.MEM_IMAGE: brushCurrent = regionSelected ? _brushTypeImageSelected : _brushTypeImage; break;
			case Native.PAGE_TYPE.MEM_MAPPED: brushCurrent = regionSelected ? _brushTypeMappedSelected : _brushTypeMapped; break;
			case Native.PAGE_TYPE.MEM_PRIVATE: brushCurrent = regionSelected ? _brushTypePrivateSelected : _brushTypePrivate; break;
			}
			return brushCurrent;
		}

		private static Brush ViewModePageState (RegionInfo ri, bool regionSelected)
		{
			Brush brushCurrent = _brushStateFree;
			switch (ri.State)
			{
			case Native.PAGE_STATE.MEM_FREE: brushCurrent = _brushStateFree; break;
			case Native.PAGE_STATE.MEM_RESERVE: brushCurrent = regionSelected ? _brushStateReservedSelected : _brushStateReserved; break;
			case Native.PAGE_STATE.MEM_COMMIT: brushCurrent = regionSelected ? _brushStateCommitedSelected : _brushStateCommited; break;
			}
			return brushCurrent;
		}

    private void CreateBitmap()
    {
      if (bmp == null)
      {
        bmp = new Bitmap (_pb.ClientSize.Width - _sb.Width - 2, _pb.ClientSize.Height, PixelFormat.Format32bppArgb);
        _pb.Image = bmp;
      }
    }

    private void _pb_SizeChanged(object sender, EventArgs e)
    {
      //lock (bmp)
      {
        if (bmp != null)
        {
          bmp.Dispose ();
          bmp = null;
        }
      }
      CreateBitmap ();
      UpdateViewData();
      UpdateScrollBar();
      UpdateBitmapView();
    }

    private void _zoom_ValueChanged(object sender, EventArgs e)
    {
      UpdateViewData();
      UpdateScrollBar();
      UpdateBitmapView();
    }

		private void _btnZoomIn_Click (object sender, EventArgs e)
		{
			if (_iZoom < 10) _iZoom++;
      UpdateViewData();
      UpdateScrollBar();
      UpdateBitmapView();
		}

		private void _btnZoomOut_Click (object sender, EventArgs e)
		{
			if (_iZoom > 1) _iZoom--;
			UpdateViewData ();
			UpdateScrollBar ();
			UpdateBitmapView ();
		}

    private void _sb_ValueChanged(object sender, EventArgs e)
    {
      UpdateViewData();
      UpdateBitmapView();
    }

    private void _pb_MouseDown(object sender, MouseEventArgs e)
    {
      if (_doc == null) return;

      // Find selected page
			int pointedPage = _iFirstVisiblePage + (e.X / _iZoom) + (e.Y / _iZoom) * _iNbCols;
      if (pointedPage == _doc.SelectedPage)
      {
        _doc.SelectedAddress = MemoryAddress.Invalid;
      }
      else
      {
        _doc.SelectedAddress = new MemoryAddress(pointedPage);
      }

      // Update
      UpdateBitmapView();
		}

    private RegionInfo _pointedRegion = null;
    private void _pb_MouseMove (object sender, MouseEventArgs e)
    {
      if (_doc == null)
      {
        _pointedRegion = null;
        return;
      }

      int pointedPage = _iFirstVisiblePage + (e.X / _iZoom) + (e.Y / _iZoom) * _iNbCols;
      int region = _doc.FindRegion (pointedPage);

      RegionInfo range = region != -1 ? _doc.AddressRanges [region] : null;

      if (range != _pointedRegion)
      {
        _pointedRegion = range;
        this.UpdateBitmapView ();
      }

      if (range!=null && range.Description!=string.Empty)
      {
				int firstPage = range.Start.Page;
				int X = ((firstPage - _iFirstVisiblePage) % _iNbCols) * _iZoom;
				int Y = ((firstPage - _iFirstVisiblePage) / _iNbCols) * _iZoom;

				using (Graphics g = _pb.CreateGraphics ())
				{
					Size size = g.MeasureString (range.Description, this.Font).ToSize ();
					Y -= size.Height;
				}
				toolTipMap.Show (range.Description, this, e.X, e.Y);
      }
      else
      {
        toolTipMap.Hide (this);
      }
    }

    private void _pb_MouseLeave (object sender, EventArgs e)
    {
      _pointedRegion = null;
      this.UpdateBitmapView ();
      toolTipMap.Hide (this);
    }


    private void _btnType_CheckedChanged (object sender, EventArgs e)
		{
			if (((RadioButton)sender).Checked)
			{
				_viewMode = ViewMode.PageType;
				UpdateBitmapView ();
			}
		}

		private void _btnState_CheckedChanged (object sender, EventArgs e)
		{
			if (((RadioButton)sender).Checked)
			{
				_viewMode = ViewMode.PageState;
				UpdateBitmapView ();
			}
		}

		private void _btnProtect_CheckedChanged (object sender, EventArgs e)
		{
			if (((RadioButton)sender).Checked)
			{
				_viewMode = ViewMode.PageProtection;
				UpdateBitmapView ();
			}
		}

		private void _btnUnknown_CheckedChanged (object sender, EventArgs e)
		{
			if (((RadioButton)sender).Checked)
			{
				_viewMode = ViewMode.PageIdentification;
				UpdateBitmapView ();
			}
		}

		#endregion
		#region List-related method

    private enum ColumnDescriptor
    {
			Start,
			Size,
			Usage,
			Description,
			State,
			Protect,
			Type,
			Base
    }

    private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
    {
      SortList((ColumnDescriptor)listView1.Columns[e.Column].Tag);
    }

    private void SortList(ColumnDescriptor column)
    {
      _sortedList.Sort(delegate(RegionInfo x, RegionInfo y)
      {
        if (x == null)
        {
          // If x is null and y is null, they're equal. 
          if (y == null) return 0;

          // If x is null and y is not null, y is greater. 
          return -1;
        }

        // If x is not null and y is null, x is greater.
        if (y == null) return 1;

        switch (column)
        {
        case ColumnDescriptor.Start:
          return x.Start.CompareTo(y.Start);
        case ColumnDescriptor.Size:
          return x.Size.CompareTo(y.Size);
        case ColumnDescriptor.Usage:
          return x.Usage.CompareTo(y.Usage);
        case ColumnDescriptor.Description:
          return x.Description.CompareTo(y.Description);
        case ColumnDescriptor.State:
          return x.State.CompareTo(y.State);
        case ColumnDescriptor.Protect:
          return x.Protect.CompareTo(y.Protect);
        case ColumnDescriptor.Type:
          return x.Type.CompareTo(y.Type);
        case ColumnDescriptor.Base:
          return x.Base.CompareTo(y.Base);
        }
        return 0;
      });

      ClearCache();
      listView1.Refresh();
    }

    private void UpdateView()
		{
      _sortedList = new List<RegionInfo>(_doc.AddressRanges);
      ClearCache();
      listView1.VirtualListSize = _sortedList.Count;

      foreach (ColumnHeader ch in listView1.Columns)
			{
				ch.Width = -2;
			}
		}


    private ListViewItem GetListItem(int index)
    {
      RegionInfo ri = _sortedList[index];

      ListViewItem item = new ListViewItem(new string[] {
					ri.Start.ToString(),	// Start
					"0x" + ri.Size.ToString("X8"),	// Length
					ri.Usage.ToString(),
					ri.Description,
					ri.State.ToString(),
					ri.Protect.ToString(),
					ri.Type.ToString(),
					ri.Base.ToString(),	// Region
				});
      item.Tag = ri;
      switch (ri.State)
      {
        case Native.PAGE_STATE.MEM_FREE:
          item.ForeColor = Color.LightGray;
          break;

        case Native.PAGE_STATE.MEM_RESERVE:
          item.ForeColor = Color.OrangeRed;
          break;

        case Native.PAGE_STATE.MEM_COMMIT:
          item.ForeColor = Color.DarkGreen;
          break;
      }

      return item;
    }

    private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
    {
      // If we have the item cached, return it. Otherwise, recreate it.
      if (_lviCache != null && e.ItemIndex >= _firstCacheItem && e.ItemIndex < _firstCacheItem + _lviCache.Length)
      {
        e.Item = _lviCache[e.ItemIndex - _firstCacheItem];
      }
      else
      {
        e.Item = GetListItem(e.ItemIndex);
      }
    }

    private void ClearCache()
    {
      _lviCache = null;
    }

    private void listView1_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e)
    {
      // Only recreate the cache if we need to.
      if (_lviCache != null && e.StartIndex >= _firstCacheItem && e.EndIndex <= _firstCacheItem + _lviCache.Length)
        return;

      _firstCacheItem = e.StartIndex;
      int length = e.EndIndex - e.StartIndex + 1;

      _lviCache = new ListViewItem[length];
      for (int i = 0; i < length; i++)
      {
        _lviCache[i] = GetListItem(_firstCacheItem + i);
      }
    }

		private void listView1_SelectedIndexChanged (object sender, EventArgs e)
		{
      MemoryAddress ma = MemoryAddress.Invalid;

			if (listView1.SelectedIndices.Count == 1)
			{
				RegionInfo ri = (RegionInfo)listView1.Items[listView1.SelectedIndices [0]].Tag;
				ma = ri.Start;
			}

			_doc.SelectedAddress = ma;
		}

    private void listView1_DoubleClick(object sender, EventArgs e)
    {
      if (listView1.SelectedIndices.Count > 0)
      {
        RegionInfo ri = (RegionInfo)listView1.Items[listView1.SelectedIndices[0]].Tag;
        if(ri.Related.Count>0)
        {
          (new PropertyForm(ri.Related[0])).Show();
        }
      }
    }

		#endregion
    #region Doc events

    private void _doc_PageSelectionChanged(object sender, PageSelectionChangeEventArgs e)
    {
      if (!_disableNavigation)
      {
        _navIndex++;
        if (_navIndex < _navigation.Count)
        {
          _navigation.RemoveRange(_navIndex, _navigation.Count - _navIndex);
        }
        _navigation.Add(e.NewValue);
      }

			if (_showMap)
			{
				UpdateScrollBar ();
				UpdateBitmapView ();
			}
    }

		private void _doc_RegionSelectionChanged (object sender, PageSelectionChangeEventArgs e)
		{
			if(_showMap || e.NewValue==-1) return;

      //foreach (ListViewItem lvi in listView1.Items)
      //{
      //  RegionInfo ri = (RegionInfo)lvi.Tag;
      //  if (ri == _doc.SelectedRange)
      //  {
      //    lvi.Selected = true;
      //    lvi.EnsureVisible ();
      //    break;
      //  }
      //}
		}

    private void _doc_DataUpdated (object sender, EventArgs e)
    {
			if (_showMap)
			{
				Invoke((MethodInvoker)delegate() {
					//listView1.Items.Clear ();
					UpdateViewData ();
					UpdateScrollBar ();
					UpdateBitmapView ();
				});
			}
			else
			{
				Invoke ((MethodInvoker)delegate ()
				{
					UpdateView ();
				});
			}
    }

    #endregion
    #region Buttons events

    private void _btnPrev_Click(object sender, EventArgs e)
    {
      if (_navIndex == 0) return;
      _navIndex--;
      _disableNavigation = true;
      _doc.SelectedPage = _navigation[_navIndex];
      _disableNavigation = false;
    }

    private void _btnNext_Click(object sender, EventArgs e)
    {
      if (_navIndex >= _navigation.Count - 1) return;
      _navIndex++;
      _disableNavigation = true;
      _doc.SelectedPage = _navigation[_navIndex];
      _disableNavigation = false;
    }

    private void _btnUpdate_Click(object sender, EventArgs e)
    {
      _doc.UpdateData();
    }

		private void _btnAutoUpdate_CheckedChanged (object sender, EventArgs e)
    {
			_doc.AutoUpdate = _btnAutoUpdate.Checked;

			toolTip1.Hide (_btnAutoUpdate);
			toolTip1.SetToolTip (_btnAutoUpdate, _doc.AutoUpdate ? "Disable auto-update" : "Enable auto-update");

    }

		private void _btnListMap_Click (object sender, EventArgs e)
		{
			toolTip1.Hide (_btnListMap);
			_showMap = ((string)_btnListMap.Tag == "list");

			if (_showMap)
			{
				listView1.Visible = false;

				_btnProtect.Visible = true;
				_btnState.Visible = true;
				_btnType.Visible = true;
        _btnUnknown.Visible = true;
				_pb.Visible = true;
				_sb.Visible = true;
				_btnZoomIn.Visible = true;
				_btnZoomOut.Visible = true;

				_btnListMap.Tag = "map";
				_btnListMap.Image = global::VMExplorer.Properties.Resources.ViewList;

				toolTip1.SetToolTip (_btnListMap, "View list");
			}
			else
			{
				listView1.Visible = true;

				_btnProtect.Visible = false;
				_btnState.Visible = false;
				_btnType.Visible = false;
        _btnUnknown.Visible = false;
        _pb.Visible = false;
				_sb.Visible = false;
				_btnZoomIn.Visible = false;
				_btnZoomOut.Visible = false;

				_btnListMap.Tag = "list";
				_btnListMap.Image = global::VMExplorer.Properties.Resources.ViewMap;

				toolTip1.SetToolTip (_btnListMap, "View map");
			}

			_doc_DataUpdated (null, null);
    }

    #endregion
  }

}
